package com.codewsq.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理举例
 * 动态代理：动态代理是在不确定被代理类的情况下，根据被代理类的类型动态的生成一个代理类
 *
 * @author codewsq
 * @date 2021/8/14 0014 14:16
 */
public class ProxyTest {
    public static void main(String[] args) {
        // proxyInstance:代理类的对象
        ClothesFactory proxyInstance = (ClothesFactory) ProxyFactory.getProxyInstance(new HongXingErke());
        proxyInstance.produceClothe();

        System.out.println("\n*******************************************************************\n");

        Human human = (Human) ProxyFactory.getProxyInstance(new SuperMan());
        human.getBelief();
        String eat = human.eat("水饺");

        System.out.println("获取到的方法返回值：" + eat);
    }
}


/**
 * 实现动态代理的两个问题，要点：
 * 1. 如何根据加载到内存中的被代理类 动态的创建一个代理类
 * 2. 如何让当代理类调用方法a时，动态的去调用被代理类中的同名方法a
 */
class ProxyFactory {

    // 1. 调用此方法可以返回一个代理类的对象， 解决问题一

    /**
     * 获取一个代理类的对象实例
     *
     * @param obj 被代理类的对象实例
     * @return java.lang.Object 代理类的对象
     * @author codewsq
     * @date 2021/8/14 0014 14:33
     */
    public static Object getProxyInstance(Object obj) {
        MyInvocationHandler handler = new MyInvocationHandler(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }

}

class MyInvocationHandler implements InvocationHandler {

    private Object obj; // 被代理类的对象实例

    public MyInvocationHandler(Object obj) {
        this.obj = obj;
    }

    /**
     * 当我们通过代理类的对象，调用方法a时，就会自动的调用如下的方法：invoke()，解决问题二
     * 将被代理类要执行的方法a的功能就声明在invoke()中
     *
     * @param proxy  代理类的对象实例
     * @param method 即为代理类对象调用的方法，此方法也就作为了被代理类对象要调用的方法
     * @param args   调用方法时所传入的参数
     * @return java.lang.Object 调用被代理类方法的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        AopUtils aopUtils = new AopUtils();
        aopUtils.beforeMethod();
        // returnValue: 调用被代理类方法的返回值
        Object returnValue = method.invoke(obj, args);
        aopUtils.afterMethod();
        return returnValue;
    }
}

/**
 * 通用方法，类似于aop中的固定方法，在运行切面涉及到的不同方法时，固定会运行的方法
 */
class AopUtils {

    public void beforeMethod() {
        System.out.println("------------进入代理方法前。。进入切点------------");
    }

    public void afterMethod() {
        System.out.println("------------代理方法完成后。。离开切点------------");
    }
}